#include "WarGameLib.h"


wgGeoMipmap::wgGeoMipmap(wgHeightmap *h)
{
	hm = h;
	PatchSize = 17;
	NumPatchesX = (int) ceilf( hm->getWidth() / (float) PatchSize);
	NumPatchesZ = (int) ceilf( hm->getHeight() / (float) PatchSize);
	NumPatches = NumPatchesX * NumPatchesZ;
	patches = new wgGeoMMPatch[NumPatches];

	MaxLOD = 0;
	int divisor = PatchSize-1;
	while(divisor > 2)
	{
		divisor = divisor >> 1;
		MaxLOD++;
	}

	cout << "Max CLOD level is " << MaxLOD << endl;

}

void wgGeoMipmap::Update(wgCamera& cam)
{

	vector3_t eye = cam.getPosition();


	wgGeoMMPatch* pPatch = patches;

	for(int z = 0; z < NumPatchesZ; z++)
	{
		for(int x = 0; x < NumPatchesX; x++)
		{
			float fX = (x * PatchSize + PatchSize / 2.0f);
				float fZ = ( z * PatchSize + PatchSize / 2.0f );
			float value = (float) hm->getHeightValue( (int) fX,(int)fZ  );
			
			float dist = (float) sqrt( SQUARE( fX - eye.x ) + SQUARE( value - eye.y ) + SQUARE( fZ - eye.z ));

			//cout << dist << " ";

			pPatch->distance = dist;
			if(dist < 500)
				pPatch->lod = 0;
			else if(dist < 1000)
				pPatch->lod = 1;
			else if(dist < 3000)
				pPatch->lod = 2;
			else
				pPatch->lod = 3;


			pPatch++;
		}
	}
}


wgGeoMipmap::~wgGeoMipmap()
{
	if(patches!=0)
	{
		delete[] patches;
		patches = 0;
	}
}

void wgGeoMipmap::Render(wgCamera &cam)
{

	hm->BindTextures();
	int x,z;

	for( z=0; z<NumPatchesZ; z++ )
	{
		for( x=0; x<NumPatchesX; x++ )
		{
			wgGeoMMPatch* pPatch = getPatch(x,z);
			RenderPatch(pPatch, x, z );		
		}
	}

	//hm->Render();

}

wgGeoMMPatch* wgGeoMipmap::getPatch(int x, int z)
{
	return &patches[getPatchNum(x,z)];
}

int wgGeoMipmap::getPatchNum(int x, int z)
{
	return z * NumPatchesZ + x;
}

void wgGeoMipmap::RenderPatch(wgGeoMMPatch *pPatch, int PX, int PZ)
{

	SGEOMM_NEIGHBOR patchNeighbor;
	SGEOMM_NEIGHBOR fanNeighbor;
	float fSize;
	float fHalfSize;
	float x, z;
	int iPatch= getPatchNum( PX, PZ );
	int iDivisor;
	int iLOD;

	//find out information about the patch to the current patch's left, if the patch is of a
	//greater detail or there is no patch to the left, we can render the mid-left vertex
	if( patches[getPatchNum( PX-1, PZ )].lod<=patches[iPatch].lod || PX==0 )
		patchNeighbor.m_bLeft= true;
	else
		patchNeighbor.m_bLeft= false;

	//find out about the upper patch
	if( patches[getPatchNum( PX, PZ+1 )].lod<=patches[iPatch].lod || PZ==NumPatchesZ )
		patchNeighbor.m_bUp= true;
	else
		patchNeighbor.m_bUp= false;

	//find out about the right patch
	if( patches[getPatchNum( PX+1, PZ )].lod<=patches[iPatch].lod || PX==NumPatchesX )
		patchNeighbor.m_bRight= true;
	else
		patchNeighbor.m_bRight= false;

	//find out about the lower patch
	if( patches[getPatchNum( PX, PZ-1 )].lod<=patches[iPatch].lod || PZ==0 )
		patchNeighbor.m_bDown= true;
	else
		patchNeighbor.m_bDown= false;

	//we need to determine the distance between each triangle-fan that
	//we will be rendering
	iLOD    = patches[getPatchNum( PX, PZ )].lod+1;
	fSize   = ( float )PatchSize;
	iDivisor= PatchSize-1;


switch(iLOD)
{
case 1:
	glColor3ub(255, 255, 255);
	break;

case 2:
	glColor3ub(255, 0, 0);
	break;

case 3:
	glColor3ub(0, 255, 0);
	break;

default:
	glColor3ub(0, 0, 255);
	break;

}


	//find out how many fan divisions we are going to have
	while( --iLOD>-1 )
		iDivisor= iDivisor>>1;

	//the size between the center of each triangle fan
	fSize/= iDivisor;

	//half the size between the center of each triangle fan (this will be
	//the size between each vertex)
	fHalfSize= fSize/2.0f;
	for( z=fHalfSize; ( ( int )( z+fHalfSize ) )<PatchSize+1; z+=fSize )
	{
		for( x=fHalfSize; ( ( int )( x+fHalfSize ))<PatchSize+1; x+=fSize )
		{
			//if this fan is in the left row, we may need to adjust it's rendering to
			//prevent cracks
			if( x==fHalfSize )
				fanNeighbor.m_bLeft= patchNeighbor.m_bLeft;
			else
				fanNeighbor.m_bLeft= true;

			//if this fan is in the bottom row, we may need to adjust it's rendering to
			//prevent cracks
			if( z==fHalfSize )
				fanNeighbor.m_bDown= patchNeighbor.m_bDown;
			else
				fanNeighbor.m_bDown= true;

			//if this fan is in the right row, we may need to adjust it's rendering to
			//prevent cracks
			if( x>=( PatchSize-fHalfSize ) )
				fanNeighbor.m_bRight= patchNeighbor.m_bRight;
			else
				fanNeighbor.m_bRight= true;

			//if this fan is in the top row, we may need to adjust it's rendering to
			//prevent cracks
			if( z>=( PatchSize-fHalfSize ) )
				fanNeighbor.m_bUp= patchNeighbor.m_bUp;
			else
				fanNeighbor.m_bUp= true;

			//render the triangle fan
			RenderFan( ( PX*PatchSize )+x, ( PZ*PatchSize )+z,
					   fSize, fanNeighbor );
		}
	}

}


void  wgGeoMipmap::RenderFan(float cX, float cZ, float fSize, SGEOMM_NEIGHBOR& neighbor)
{

	float fTexLeft, fTexBottom, fMidX, fMidZ, fTexRight, fTexTop;
	float fHalfSize= fSize/2.0f;

	//calculate the texture coordinates if we're not doing multitexturing, but still detail mapping
	
		//calculate the texture coordinates
		fTexLeft  = ( ( float )fabs( cX-fHalfSize )/ hm->getWidth() );
		fTexBottom= ( ( float )fabs( cZ-fHalfSize )/ hm->getHeight() );
		fTexRight = ( ( float )fabs( cX+fHalfSize )/ hm->getWidth() );
		fTexTop	  = ( ( float )fabs( cZ+fHalfSize )/ hm->getHeight() );

		fMidX= ( ( fTexLeft+fTexRight )/2 );
		fMidZ= ( ( fTexBottom+fTexTop )/2 );
	


	//begin a new triangle strip
	glBegin( GL_TRIANGLE_FAN );
		//render the CENTER vertex
		RenderVertex( cX, cZ, fMidX, fMidZ );

		//render the LOWER-LEFT vertex
		RenderVertex( cX-fHalfSize, cZ-fHalfSize, fTexLeft, fTexBottom );		

		//only render the next vertex if the left patch is NOT of a lower LOD
		if( neighbor.m_bLeft )
		{
			RenderVertex( cX-fHalfSize, cZ, fTexLeft, fMidZ );
			
		}
	
		//render the UPPER-LEFT vertex
		RenderVertex( cX-fHalfSize, cZ+fHalfSize, fTexLeft, fTexTop );
		

		//only render the next vertex if the upper patch is NOT of a lower LOD
		if( neighbor.m_bUp )
		{
			RenderVertex( cX, cZ+fHalfSize, fMidX, fTexTop );
			
		}

		//render the UPPER-RIGHT vertex
		RenderVertex( cX+fHalfSize, cZ+fHalfSize, fTexRight, fTexTop );
		

		//only render the next vertex if the right patch is NOT of a lower LOD
		if( neighbor.m_bRight )
		{
			//render the MID-RIGHT vertex
			RenderVertex( cX+fHalfSize, cZ, fTexRight, fMidZ );
			
		}

		//render the LOWER-RIGHT vertex
		RenderVertex( cX+fHalfSize, cZ-fHalfSize, fTexRight, fTexBottom );
		

		//only render the next vertex if the bottom patch is NOT of a lower LOD
		if( neighbor.m_bDown )
		{
			//render the LOWER-MID vertex
			RenderVertex( cX, cZ-fHalfSize, fMidX, fTexBottom );	
			
		}

		//render the LOWER-LEFT vertex
		RenderVertex( cX-fHalfSize, cZ-fHalfSize, fTexLeft, fTexBottom );
		

	//end the triangle strip
	glEnd( );
	
}


void wgGeoMipmap::RenderVertex( float x, float z, float u, float v )
{
	unsigned char ucColor= hm->getHeightValue( ( int )x, ( int )z );

	

	//send the texture coordinates to the rendering API	
	
	glTexCoord2f(u, v );

	//output the vertex to the rendering API
	glVertex3f( x,
				(float) ucColor,
				z );

	//increase the number of vertices rendered
	
}
